/**
 * 
 */
package com.ubo.visu.parser;

import java.util.HashMap;
import java.util.Map;

import com.ubo.visu.model.EEntityType;
import com.ubo.visu.model.EModelAssociationType;
import com.ubo.visu.model.MemberAttribute;
import com.ubo.visu.model.MemberMethod;
import com.ubo.visu.model.ModelAssociation;
import com.ubo.visu.model.ModelGraph;
import com.ubo.visu.model.ModelNode;

/**
 * @author saludegu
 * 
 */
public class DotParser implements DotConstants {

	/**
	 * String contenant les instructions dot.
	 */
	protected final String mDotContent;

	/**
	 * Graphe.
	 */
	protected ModelGraph mGraph;

	/**
	 * Map de nodes en fonction des strings.
	 */
	protected Map<String, ModelNode> mMapNodes;

	/**
	 * Model node en cours de traitement.
	 */
	protected ModelNode mCurrModelNode;

	/**
	 * Model node en cours de traitement.
	 */
	protected MemberAttribute mCurrMemberAttribute;

	/**
	 * 
	 * Constructor
	 * 
	 * @param dotContent
	 */
	public DotParser(String dotContent) {
		mDotContent = dotContent;
		mGraph = new ModelGraph();
		mMapNodes = new HashMap<String, ModelNode>();
	}

	/**
	 * Renvoie le graphe généré.
	 * 
	 * @return
	 */
	public ModelGraph getGraph() {
		return mGraph;
	}

	/**
	 * Parse les associations.
	 * 
	 * @param tabStr
	 */
	public void parseAssociations(String line) {
		if (line.contains(ASSOCIATION_DIR_DOT_KEYWORD)) {
			parseAssociation(line.split(ASSOCIATION_DIR_DOT_KEYWORD));
		} else if (line.contains(ASSOCIATION_DOT_KEYWORD)) {
			parseAssociation(line.split(ASSOCIATION_DOT_KEYWORD));
		}
	}

	/**
	 * Parse une ligne d'associations.
	 * 
	 * @param tabStr
	 */
	public void parseAssociation(String[] tabStr) {
		for (int index = 0; index < tabStr.length; index++) {
			if (index + 1 < tabStr.length) {
				String currStr = tabStr[index + 1];
				if (tabStr[index + 1].endsWith(";")) {
					currStr = tabStr[index + 1].substring(0,
							tabStr[index + 1].length() - 1);
				}
				String[] descAssoc = currStr.split("\"");
				String className = descAssoc[0];
				/*
				 * if (!mMapNodes.containsKey(tabStr[index])) {
				 * mMapNodes.put(tabStr[index], new ModelNode(tabStr[index],
				 * EEntityType.CLASS));
				 * mGraph.addModelNode(mMapNodes.get(tabStr[index])); } if
				 * (!mMapNodes.containsKey(className)) {
				 * mMapNodes.put(className, new ModelNode(className,
				 * EEntityType.CLASS));
				 * mGraph.addModelNode(mMapNodes.get(className)); }
				 */
				// Recherche du type de l'association
				// [0] = cardFrom, [1] = cardTo, [2] = typeAssoc
				String[] members = descAssoc[1].split(":");
				String assocType = members[2].substring(0, members[2].length());
				mGraph.addAssociatioNode(new ModelAssociation(mMapNodes
						.get(tabStr[index]), mMapNodes.get(className),
						getModelAssoc(assocType), members[0], members[1]));
			}
		}
	}

	/**
	 * Renvoie le type de l'association.
	 * 
	 * @param str
	 * @return
	 */
	protected EModelAssociationType getModelAssoc(String str) {
		EModelAssociationType res = EModelAssociationType.SIMPLE;
		if (str.equalsIgnoreCase(EModelAssociationType.EXTENDS.toString())) {
			res = EModelAssociationType.EXTENDS;
		} else if (str.equalsIgnoreCase(EModelAssociationType.IMPLEMENTS
				.toString())) {
			res = EModelAssociationType.IMPLEMENTS;
		}
		return res;
	}

	/**
	 * Parse la description d'un membre.
	 * 
	 * @param line
	 */
	void parseMemberDesc(String line) {
		String[] tabStr = line.split(MEMBER_DESC_TOKEN);
		if (tabStr[0].equalsIgnoreCase(EMemberDescription.ATTRIBUTETYPE
				.toString())) {
			if (tabStr[1].equals(ATTRIBUTE_KEYWORD)) {
				if (mCurrMemberAttribute != null)
					mCurrModelNode.addModelAttribute(mCurrMemberAttribute);
				mCurrMemberAttribute = new MemberAttribute();
			} else if (tabStr[1].equals(METHODE_KEYWORD)) {
				System.out.println(line);
			}
		} else if (tabStr[0].equalsIgnoreCase(EMemberDescription.NAME
				.toString())) {
			mCurrMemberAttribute.setName(tabStr[1]);
		} else if (tabStr[0].equalsIgnoreCase(EMemberDescription.TYPE
				.toString())) {
			mCurrMemberAttribute.setType(tabStr[1]);
		} else if (tabStr[0].equalsIgnoreCase(EMemberDescription.VISIBILITY
				.toString())) {
			mCurrMemberAttribute.setVisibility(tabStr[1]);
		}
	}

	/**
	 * Parse la description d'une classe.
	 * 
	 * @param line
	 */
	void parseClassDesc(String line) {
		if (line.contains("shape")) {
			String className = line.split("type")[0];
			String classType = line.split("type")[1];
			String type = classType.split("shape")[0];
			type = type.substring(type.indexOf("=") + 1, type.length() - 1);
			String classDesc = line.split("shape")[1];
			className = className.substring(0, className.length() - 1);
			if (!mMapNodes.containsKey(className)) {
				mMapNodes.put(className,
						new ModelNode(className, EEntityType.wrapp(type)));
				mGraph.addModelNode(mMapNodes.get(className));
			}
			mCurrModelNode = mMapNodes.get(className);
			int startLabelIndex = classDesc.indexOf("\"");
			int endLabelIndex = classDesc.indexOf("\"", startLabelIndex + 1);
			String label = classDesc.substring(startLabelIndex + 1,
					endLabelIndex);
			// Recherche des attributs
			int startAttrIndex = label.indexOf("{");
			int endAttrIndex = label.indexOf("}");
			String attrs = label.substring(startAttrIndex + 1, endAttrIndex);
			String[] tabAttr = attrs.split("#");
			for (String attr : tabAttr) {
				if (!attr.isEmpty()) {
					String[] tabMembers = attr.split(" ");
					if (tabMembers.length == 3) {
						mCurrModelNode.addModelAttribute(new MemberAttribute(
								tabMembers[0], tabMembers[1], tabMembers[2]));
					} else {
						String[] modifiers = new String[tabMembers.length - 3];
						for (int i = 1; i < tabMembers.length - 2; i++) {
							modifiers[i - 1] = tabMembers[i];
						}
						mCurrModelNode.addModelAttribute(new MemberAttribute(
								tabMembers[0],
								tabMembers[tabMembers.length - 2],
								tabMembers[tabMembers.length - 1], modifiers));
					}

				}
			}
			// Recherche des m�thodes
			int startMethIndex = label.indexOf("{", endAttrIndex);
			int endMethIndex = label.indexOf("}", startMethIndex + 1);
			String meths = label.substring(startMethIndex + 1, endMethIndex);
			String[] tabMeths = meths.split("#");
			for (String meth : tabMeths) {
				if (!meth.isEmpty()) {
					String[] tabMembers = meth.split(" ");
					if (tabMembers.length == 3) {
						mCurrModelNode.addModelMethod(new MemberMethod(
								tabMembers[0], tabMembers[1], tabMembers[2]));
					} else {
						String[] modifiers = new String[tabMembers.length - 3];
						for (int i = 1; i < tabMembers.length - 2; i++) {
							modifiers[i - 1] = tabMembers[i];
						}
						mCurrModelNode.addModelMethod(new MemberMethod(
								tabMembers[0],
								tabMembers[tabMembers.length - 2],
								tabMembers[tabMembers.length - 1], modifiers));
					}
				}
			}
		} else {
			String className = line.split("type")[0];
			String classType = line.split("type")[1];
			String type = classType.substring(classType.indexOf("=") + 1,
					classType.length() - 3);
			className = className.substring(0, className.length() - 1);
			if (!mMapNodes.containsKey(className)) {
				mMapNodes.put(className,
						new ModelNode(className, EEntityType.wrapp(type)));
				mGraph.addModelNode(mMapNodes.get(className));
			}
			mCurrModelNode = mMapNodes.get(className);
		}
	}

	/**
	 * Parse la string et rempli le modelGraph.
	 */
	public void parse() {
		// Divise en ligne.
		String[] tabStr = mDotContent.split("\n");
		// Parcours de chaque ligne.
		for (String line : tabStr) {
			// Check subgraph
			if (line.startsWith(SUBGRAPH_DOT_KEYWORD)) {

			} else if (line.contains(ASSOCIATION_DIR_DOT_KEYWORD)
					|| line.contains(ASSOCIATION_DOT_KEYWORD)) {
				parseAssociations(line);
			} else if (line.contains("[") && line.contains("]")) {
				parseClassDesc(line);
			}
		}
	}
}
